﻿using HCLSMod;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace LNamePipe
{
    public delegate void NamePipeEventCallBack(object sender, ref byte[] RecData);

    public enum TAnalyseDatasType
    {
        ADT_SerRec = 1,             // 服务器收到数据
        ADT_SerAnswer = 2,          // 服务器应答数据
        ADT_CliSend = 3,            // 客户端发送数据
        ADT_CliRec = 4,             // 客户端收到数据
    }

    public class NamePipeBase
    {

        public event NamePipeEventCallBack OnRecData;

        protected void RunOnRecData(object sender, ref byte[] RecData)
        {
            if (OnRecData != null)
                OnRecData(sender, ref RecData);
        }
        /// <summary>
        /// 错误事件记录
        /// </summary>
        public ZLErrorRecord ErrorRecord = new ZLErrorRecord();
        /// <summary>
        /// 功能模块的GUID，用于标示单元，或客户端连接，所有发送命令，都携带该标示
        /// </summary>
        protected Guid fUserToken = Guid.NewGuid();
        /// <summary>
        /// 功能模块的GUID，用于标示单元，或客户端连接，所有发送命令，都携带该标示
        /// </summary>
        public Guid UserToken { get { return fUserToken; } }

        protected const string DefaultPipeName = "NamePipe_Sample";
        /// <summary>
        /// 命名管道名称，对应不用子类型，用于不同目的，可以是管道服务器名称，也可以是要连接的目标管道命令
        /// </summary>
        protected string fPipeName = DefaultPipeName;
        /// <summary>
        /// 命名管道名称
        /// </summary>
        public string PipeName { get { return fPipeName; } }
                
        /// <summary>
        /// 接受缓冲区大小，设置的新值必须大于0，否则使用原设置
        /// </summary>
        protected int fRecBufSize = 4000;
        /// <summary>
        /// 接受缓冲区大小，设置的新值必须大于0，否则使用原设置
        /// </summary>
        public int RecBufSize
        {
            get { return fRecBufSize; }
            set
            {
                if (value > 0)
                    fRecBufSize = value;
            }
        }
        /// <summary>
        /// 分析，打包接收发送数据基础函数，便于子类功能调用
        /// </summary>
        /// <param name="fRTDatas">要发送或接收的数据，成功处理返回处理结果</param>
        /// <param name="AnalyseType">分析类型</param>
        /// <returns>成功处理返回真</returns>
        protected virtual bool AnalyseRTDatas(ref byte[] fRTDatas, TAnalyseDatasType AnalyseType)
        {
            return true;
        }
    }

    public class NPServerSocket: NamePipeBase
    {
        /// <summary>
        /// 命名管道状态，服务端：有客户端连接；客户端：已经连接到服务端
        /// </summary>
        protected bool fConnected = false;
        public bool Connected { get { return fConnected; } }
        /// <summary>
        /// 线程安全调用的根窗体对象
        /// </summary>
        public Control ParentControl { get; set; }
        /// <summary>
        /// 有客户端连接进入事件
        /// </summary>
        public event NamePipeEventCallBack OnConnectedByClient;
        /// <summary>
        /// 客户端断开事件
        /// </summary>
        public event NamePipeEventCallBack OnDisconnectedByClient;

        private NamedPipeServerStream fPipeServer;

        private BinaryReader fSReader = null;
        private BinaryWriter fSWriter = null;        

        public NPServerSocket() : this(DefaultPipeName)
        { }

        public NPServerSocket(string fPNName)
        {
            fPipeName = fPNName;
            // 使用windows的权限设置
            //PipeSecurity fPipeSa = new PipeSecurity();
            //fPipeSa.SetAccessRule(new PipeAccessRule("Everyone",
            //               PipeAccessRights.ReadWrite,
            //               System.Security.AccessControl.AccessControlType.Allow));
            //fPipeServer = new NamedPipeServerStream(fPipeName, PipeDirection.InOut, 200, PipeTransmissionMode.Byte,
            //    PipeOptions.Asynchronous, 4096, 4096, fPipeSa,HandleInheritability.None);
            // 不使用特殊的权限设置，在Windows Service中可以与桌面程序直接进行通讯
            fPipeServer = new NamedPipeServerStream(fPipeName, PipeDirection.InOut, 200, PipeTransmissionMode.Byte,
                PipeOptions.Asynchronous);
        }
        /// <summary>
        /// 启动管道，
        /// 注意：
        /// 1、管道创建后是不启动的，需要调用该命令才可以启动管道
        /// 2、当前管道被客户端连接后，就不能够响应其他客户端的连接请求；
        /// 但同一名称下可以创建多个命名管道，只要有一个命名管道空闲，就可以相应客户端的连接请求；
        /// </summary>
        public void StartNamePipe()
        {
            try
            {
                AsyncCallback aa = null;
                aa = (o) =>
                {
                    try
                    {
                        // 等待客户端接入
                        fPipeServer.EndWaitForConnection(o);
                        fConnected = true;
                        // 调用事件
                        if (OnConnectedByClient != null)
                        {
                            byte[] fConnectedBuf = Encoding.UTF8.GetBytes(fPipeServer.GetAccessControl().ToString());
                            if (ParentControl != null)
                                ParentControl.Invoke((MethodInvoker)delegate { OnConnectedByClient(this, ref fConnectedBuf); });
                            else
                                OnConnectedByClient(this, ref fConnectedBuf);
                        }
                        // 初始化读取与写入
                        fSReader = new BinaryReader(fPipeServer);
                        fSWriter = new BinaryWriter(fPipeServer);
                        byte[] fRecBuf = new byte[fRecBufSize];
                        while (true)
                        {
                            int fRecCount = fSReader.Read(fRecBuf, 0, fRecBufSize);
                            if (fRecCount > 0)
                            {
                                byte[] fRecData = new byte[fRecCount];
                                for (int i = 0; i < fRecCount; i++)
                                    fRecData[i] = fRecBuf[i];
                                if (AnalyseRTDatas(ref fRecData, TAnalyseDatasType.ADT_SerRec))
                                {
                                    // 调用接受数据事件
                                    if (ParentControl != null)
                                        ParentControl.Invoke((MethodInvoker)delegate { RunOnRecData(this, ref fRecData); });
                                    else
                                        RunOnRecData(this, ref fRecData);
                                }
                                // 如果事件返回了数据，则向客户端返回新的数据
                                if ((fRecData != null)
                                    && AnalyseRTDatas(ref fRecData, TAnalyseDatasType.ADT_SerAnswer)
                                    && (fRecData.Length > 0))
                                {
                                    fSWriter.Write(fRecData, 0, fRecData.Length);
                                    fSWriter.Flush();
                                }
                            }
                            else
                            {
                                // 客户端断开连接，跳出循环
                                break;
                            }
                        }
                        fConnected = false;
                        // 启动断开连接事件继承
                        RunOnDisconnect();
                        // 断开客户端连接后调用事件
                        if (OnDisconnectedByClient != null)
                        {
                            byte[] fConnectedBuf = null;
                            if (ParentControl != null)
                                ParentControl.Invoke((MethodInvoker)delegate { OnDisconnectedByClient(this, ref fConnectedBuf); });
                            else
                                OnDisconnectedByClient(this, ref fConnectedBuf);
                        }
                        fPipeServer.Disconnect();//服务器断开，很重要！
                        fSReader = null;
                        fSWriter = null;
                        fPipeServer.BeginWaitForConnection(aa, fPipeServer);//再次等待连接，更重要！
                    }
                    catch (Exception ex)
                    {
                        ErrorRecord.PutErrorEvent(ex);
                    }
                };
                fPipeServer.BeginWaitForConnection(aa, fPipeServer);
            }
            // Catch the IOException that is raised if the pipe is broken
            // or disconnected.
            catch (Exception ex)
            {
                ErrorRecord.PutErrorEvent(ex);
            }
        }

        protected virtual void RunOnDisconnect()
        {

        }
        /// <summary>
        /// 停止命名管道
        /// </summary>
        public void StopNamePipe()
        {
            if (fPipeServer.IsConnected)
                fPipeServer.Disconnect();
            fPipeServer.Close();
        }
        /// <summary>
        /// 设置新的命名管道名称
        /// </summary>
        /// <param name="fPName"></param>
        public void SetPipeName(string fPName)
        {
            if (fPipeServer != null)
            {
                if (fConnected)
                    fPipeServer.Disconnect();
                fPipeServer.Close();
            }
            fPipeName = fPName;
            fPipeServer = new NamedPipeServerStream(fPipeName, PipeDirection.InOut, 200, PipeTransmissionMode.Byte,
                PipeOptions.Asynchronous);
        }
    }

    public class NPClientSocket: NamePipeBase
    {
        #region 成员
        /// <summary>
        /// 线程安全调用的根窗体对象
        /// </summary>
        public Control ParentControl { get; set; }
        /// <summary>
        /// 目标管道IP地址。
        /// 注意:
        /// 1、命名管道仅响应本地局域网内地址;
        /// 2、其他电脑IP上命名管道需要进行电脑用户认证，否则无法连接
        /// 默认为本地命令管道
        /// </summary>
        protected string fNPSerIP = ".";
        public string NPServerIP { get { return fNPSerIP; } }
        /// <summary>
        /// 命名管道名称
        /// </summary>
        public string NPSerName { get { return fPipeName; } }

        private NamedPipeClientStream fPipeClient;
        
        public event NamePipeEventCallBack OnConnectedToServer;
        /// <summary>
        /// 管道状态
        /// </summary>
        public bool IsConnected
        {
            get
            {
                bool fR = false;
                if (fPipeClient != null)
                    fR = fPipeClient.IsConnected;
                return fR;
            }
        }
        #endregion

        #region 构建函数
        public NPClientSocket()
        {
            RecBufSize = 4096;
        }

        public NPClientSocket(string fNPName)
        {
            RecBufSize = 4096;
            fPipeName = fNPName;
        }

        public NPClientSocket(string fSerIP, string fNPName)
        {
            RecBufSize = 4096;
            fNPSerIP = fSerIP;
            fPipeName = fNPName;
        }

        public NPClientSocket(string fSerIP, string fNPName, int fNewRecBufSize)
        {
            fNPSerIP = fSerIP;
            fPipeName = fNPName;
            RecBufSize = fNewRecBufSize;
        }
        #endregion

        #region 功能
        /// <summary>
        /// 连接命名管道
        /// </summary>
        /// <param name="fSerIP">目标地址，建议使用本地</param>
        /// <param name="fDestNPName">管道名称</param>
        /// <param name="fNewRecBufSize">接收缓冲区大小</param>
        /// <param name="fWaitNPTimeOut">连接超时设置，0为不超时</param>
        /// <returns></returns>
        public bool Connect(string fSerIP, string fDestNPName, int fNewRecBufSize, int fWaitNPTimeOut = 0)
        {
            fNPSerIP = fSerIP;
            fPipeName = fDestNPName;
            RecBufSize = fNewRecBufSize;
            fPipeClient = new NamedPipeClientStream(fNPSerIP, fPipeName, PipeDirection.InOut, PipeOptions.Asynchronous, System.Security.Principal.TokenImpersonationLevel.None);
                fPipeClient.Connect(fWaitNPTimeOut);
                // 连接成功，调用事件
                if (fPipeClient.IsConnected && (OnConnectedToServer != null))
                {
                    byte[] fConnectedBuf = new byte[0];
                    if (ParentControl != null)
                        ParentControl.Invoke((MethodInvoker)delegate { OnConnectedToServer(this, ref fConnectedBuf); });
                    else
                        OnConnectedToServer(this, ref fConnectedBuf);
                }
            // 如果连接成功，启动接收监听线程
            if (fPipeClient.IsConnected)
            {
                System.Threading.ThreadPool.QueueUserWorkItem(fRecDataThd, fPipeClient);
            }
            return this.IsConnected;
        }

        public bool Connect(string fSerIP, string fNPName, int fWaitNPTimeOut = 0)
        {
            return Connect(fSerIP, fNPName, 0, fWaitNPTimeOut);
        }

        public bool Connect(string fNPName, int fWaitNPTimeOut = 0)
        {
            return Connect(".", fNPName, 0, fWaitNPTimeOut);
        }
        /// <summary>
        /// 断开连接
        /// </summary>
        public void Disconnect()
        {
            if (this.IsConnected)
                fPipeClient.Close();
        }
        /// <summary>
        /// 向目标命名管道发送数据
        /// </summary>
        /// <param name="fSendData"></param>
        /// <returns></returns>
        public int SendData(byte[] fSendData)
        {
            int fResult = 0;
            if (IsConnected 
                && AnalyseRTDatas(ref fSendData, TAnalyseDatasType.ADT_CliSend)
                && (fSendData.Length > 0))
            {
                BinaryWriter fSw = new BinaryWriter(fPipeClient);
                fSw.Write(fSendData);
                fSw.Flush();
                fResult = fSendData.Length;
            }
            return fResult;
        }
        /// <summary>
        /// 接收返回的数据，必须放到线程中
        /// </summary>
        /// <param name="sender"></param>
        private void fRecDataThd(object sender)
        {
            if (this.IsConnected)
                using (BinaryReader fSReader = new BinaryReader((NamedPipeClientStream)sender))
                    while (true)
                    {
                        try
                        {
                            byte[] fRecBuf = new byte[fRecBufSize];
                            int fRecCount = fSReader.Read(fRecBuf, 0, fRecBufSize);
                            // 接收到数据后，调用事件
                            if (fRecCount > 0) 
                            {
                                byte[] fRecData = new byte[fRecCount];
                                for (int i = 0; i < fRecCount; i++)
                                    fRecData[i] = fRecBuf[i];
                                if (AnalyseRTDatas(ref fRecData, TAnalyseDatasType.ADT_CliRec))
                                {
                                    if (ParentControl != null)
                                        ParentControl.Invoke((MethodInvoker)delegate { RunOnRecData(this, ref fRecData); });
                                    else
                                        RunOnRecData(this, ref fRecData);
                                }
                            }
                        }
                        catch(Exception ex)
                        {
                            ErrorRecord.PutErrorEvent(ex);
                            if (this.IsConnected)
                                fPipeClient.Close();
                            break;
                        }
                    }
        }       

        #endregion
    }

    public class NPService: NamePipeBase
    {
        #region 成员
        /// <summary>
        /// 当前的所有命令管道列表
        /// </summary>
        protected List<NPServerSocket> fNamePipeList = new List<NPServerSocket>();
        /// <summary>
        /// 有客户端连接进入事件
        /// </summary>
        public event NamePipeEventCallBack OnConnectedByClient;
        /// <summary>
        /// 客户端断开事件
        /// </summary>
        public event NamePipeEventCallBack OnDisconnectedByClient;
        /// <summary>
        /// 命名管道名称
        /// 服务运行状态中，不可修改，必须停止所有服务后修改
        /// </summary>
        public string SerPipeName
        {
            get { return fPipeName; }
            set
            {
                if (!Active)
                    fPipeName = value;
            }
        }
        /// <summary>
        /// 命名管道是否启动
        /// </summary>
        public bool Active
        {
            get { return PipeCount > 0; }
            set
            {
                if (value)
                {
                    if (fNamePipeList.Count == 0)
                        fAddNewPipe(fCreateNewNPSer());
                }
                else
                {
                    if (fNamePipeList.Count > 0)
                        foreach (NPServerSocket fNPSer in fNamePipeList)
                            fNPSer.StopNamePipe();
                    fNamePipeList.Clear();
                }
            }
        }
        /// <summary>
        /// 命名管道监听接口数量
        /// </summary>
        public int PipeCount { get { return fNamePipeList.Count; } }
        /// <summary>
        /// 客户端数量
        /// </summary>
        public int ClientCount
        {
            get
            {
                int fClientCount = 0;
                if (fNamePipeList.Count > 0)
                    foreach (NPServerSocket fNPSer in fNamePipeList)
                        if (fNPSer.Connected)
                            fClientCount++;
                return fClientCount;
            }
        }

        #endregion

        public NPService()
        {
            fPipeName = "";
        }
        public NPService(string fNamePipe):this()
        {
            fPipeName = fNamePipe;
        }              
        /// <summary>
        /// 增加一个空的服务接口
        /// </summary>
        /// <param name="fNPSer"></param>
        /// <returns></returns>
        protected int fAddNewPipe(NPServerSocket fNPSer)
        {
            if (fNPSer != null)
            {
                fNPSer.OnConnectedByClient += FNPSer_OnConnectedByClient;
                fNPSer.OnDisconnectedByClient += FNPSer_OnDisconnectedByClient;
                fNamePipeList.Add(fNPSer);
                fNPSer.StartNamePipe();
            }
            return fNamePipeList.Count;
        }

        /// <summary>
        /// 创建一个服务接口
        /// </summary>
        /// <returns></returns>
        protected virtual NPServerSocket fCreateNewNPSer()
        {
            if (!string.IsNullOrEmpty(fPipeName))
            {
                NPServerSocket fNPSer = new NPServerSocket(fPipeName);
                fNPSer.OnRecData += FNPSer_OnRecData;
                return fNPSer;
            }
            return null;
        }

        private void FNPSer_OnDisconnectedByClient(object sender, ref byte[] RecData)
        {
            NPServerSocket fNPSer = (NPServerSocket)sender;
            
            if (OnDisconnectedByClient != null)
                OnDisconnectedByClient(sender, ref RecData);
        }

        private void FNPSer_OnConnectedByClient(object sender, ref byte[] RecData)
        {
            while (ClientCount >= fNamePipeList.Count)
                fAddNewPipe(fCreateNewNPSer());
            if (OnConnectedByClient != null)
                OnConnectedByClient(sender, ref RecData);
        }

        private void FNPSer_OnRecData(object sender, ref byte[] RecData)
        {
            RunOnRecData(sender, ref RecData);
        }
    }

}
